/*
 * Copyright 2003,2004 The Apache Software Foundation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package eu.jakubiak.jpa.translator;

import eu.jakubiak.jpa.translator.proxy.*;
import eu.jakubiak.jpa.translator.annotations.Translatable;
import eu.jakubiak.jpa.translator.annotations.Translate;
import eu.jakubiak.jpa.translator.discriminator.Discriminator;
import eu.jakubiak.jpa.translator.provider.Provider;
import eu.jakubiak.jpa.translator.serializer.Serializer;
import eu.jakubiak.utils.javabean.BeanProperty;
import java.lang.reflect.InvocationTargetException;
import java.util.Locale;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;

/**
 * Test classes 
 * @author jakubiak_a
 */
public class Translator {

    /**
     * Mark object allready translated
     */
    public static interface IsProxy {
    }

    /**
     * Current locale for tranlated objects
     */
    private Locale locale;
    /**
     * Provider instance 
     * this override @Translate(provider)
     */
    private Provider provider;
    /**
     * Discriminator instance
     * this override @Translate(discriminator)
     */
    private Discriminator discriminator;
    /**
     * Serializer instance
     * this override @Translate(serializer)
     */
    private Serializer serializer;

    /**
     * Create new translator
     * @param locale
     */
    public Translator(Locale locale) {
        this.locale = locale;
    }

    /**
     * Get current locale
     * @return current locale used in translator
     */
    public Locale getLocale() {
        return locale;
    }

    /**
     * Create translated proxy object
     * 
     * @param object for translation
     * @return proxy object 
     */
    public Object translate(Object object) {
        if (object == null) {
            // I don't know how to read null
            return null;
        }

        if (object instanceof IsProxy) {
            // I want to translate proxy only for objects which wasn't allready translated
            // this object was allready translated
            return object;
        }

        if (!object.getClass().isAnnotationPresent(Translatable.class)) {
            // This object is not translatable
            return object;
        }


        MethodInterceptor methodInterceptor = new TranslatorProxyMethodInterceptor(object, this);
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(object.getClass());
        enhancer.setInterfaces(new Class[]{IsProxy.class});
        enhancer.setCallback(methodInterceptor);
        return enhancer.create();
    }

    public Discriminator getDiscriminator() {
        return discriminator;
    }

    public void setDiscriminator(Discriminator discriminator) {
        this.discriminator = discriminator;
    }

    public Provider getProvider() {
        return provider;
    }

    public void setProvider(Provider provider) {
        this.provider = provider;
    }

    public Serializer getSerializer() {
        return serializer;
    }

    public void setSerializer(Serializer serializer) {
        this.serializer = serializer;
    }

    
    
    
    public Object read(BeanProperty property, Object object)
            throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {

        if(!property.isAnnotationPresent(Translate.class)) {
            return property.get(object);
        }
        
        Translate translatable = (Translate) property.getAnnotation(Translate.class);
        

        TranslationKey key = createKey(property, object);
        
        String translatedText = getProviderFor(translatable).get(key);

        Object returnValue = getSerializerFor(translatable).deserialize(property.getType(), translatedText);
        if(returnValue!=null) {
            return returnValue;
        }
        
        return property.get(object);
    }
    
    
    

    public void store(BeanProperty property, Object object, Object translatedValue)
            throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {

        if(!property.isAnnotationPresent(Translate.class)) {
            property.set(object, translatedValue);
            return;
        }
        
        Translate translatable = (Translate) property.getAnnotation(Translate.class);
        String translatedString = getSerializerFor(translatable).serialize(translatedValue);
        
        TranslationKey key = createKey(property, object);
        getProviderFor(translatable).set(key, translatedString);
    }

    
    
    
    private Provider getProviderFor(Translate translate) throws InstantiationException, IllegalAccessException {
        if (getProvider() != null) {
            return getProvider();
        }
        return (Provider) translate.provider().newInstance();
    }

    private Serializer getSerializerFor(Translate translate) throws InstantiationException, IllegalAccessException {
        if (getSerializer() != null) {
            return getSerializer();
        }
        return (Serializer) translate.serializer().newInstance();
    }

    private Discriminator getDiscriminatorFor(Translate translate) throws InstantiationException, IllegalAccessException {
        if (getDiscriminator() != null) {
            return getDiscriminator();
        }
        return (Discriminator) translate.discriminator().newInstance();
    }
    
    private TranslationKey createKey(BeanProperty property, Object object) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        Translate translatable = (Translate) property.getAnnotation(Translate.class);
        return new TranslationKey(property, object, getLocale(), getSerializerFor(translatable), getDiscriminatorFor(translatable));
    }
}
